/**
 * Copyright 2012 Simple-Jdbc-Lightweight

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
 */
package org.mlu.jdbc.simple.utils;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 *
 * @author Mlungisi Sincuba
 * @since 1.0
 */
public class ReflectionUtil {

    /**
     *
     * @param className String
     *
     * @return Object
     *
     * @throws ClassNotFoundException
     * @throws IllegalAccessException
     * @throws InstantiationException
     */
    public static Object newInstance(String className) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        Object object = null;
        try {
            object = (Object) Class.forName(className).newInstance();
        } catch (ClassNotFoundException e) {
            ClassLoader cl = Thread.currentThread().getContextClassLoader();
            object = (Object) cl.loadClass(className).newInstance();
        }
        return object;
    }

    /**
     *
     * @param object Object
     * @param fieldName String
     * @param newValue Object
     *
     * @throws NoSuchFieldException
     * @throws IllegalArgumentException
     * @throws IllegalAccessException
     */
    public static void setFieldValue(Object object, String fieldName, Object newValue) throws NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
        //Class<?> clazz = object.getClass();
        Field field = getDeclaredField(object, fieldName, true);
        field.setAccessible(true);
        field.set(object, newValue);
    }

    /**
     *
     * @param object Object
     * @param methodName String
     * @param arguments Object[]
     *
     * @return Object
     *
     * @throws NoSuchMethodException
     * @throws IllegalArgumentException
     * @throws IllegalAccessException
     * @throws InvocationTargetException
     */
    public static Object invokeMethod(Object object, String methodName, Object ...arguments) throws NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException {
        Method method = getDeclaredMethod(object, methodName, true);
        method.setAccessible(true);
        return method.invoke(object, arguments);
    }

    /**
     *
     * @param object Object
     * @param name String
     *
     * @return Method
     *
     * @throws NoSuchMethodException
     */
    public static Method getDeclaredMethod(Object object, String name, boolean searchSuperMethods) throws NoSuchMethodException {
        Class<?> clazz = object.getClass();
        Method method = clazz.getDeclaredMethod(name);

        if (method == null && searchSuperMethods) {
            while (method == null & (clazz = clazz.getSuperclass()) != null) {
                method = clazz.getDeclaredMethod(name);
            }
        }

        if (method == null) {
            throw new NoSuchMethodException();
        }

        return method;
    }

    /**
     *
     * @param object Object
     * @param name String
     * @param includeSuperFields
     *
     * @return boolean
     *
     * @throws NoSuchFieldException
     */
    public static Field getDeclaredField(Object object, String name, boolean includeSuperFields) throws NoSuchFieldException {
        Class<?> clazz = object.getClass();
        Field field = clazz.getDeclaredField(name);

        if (field == null && includeSuperFields) {
            while (field == null & (clazz = clazz.getSuperclass()) != null) {
                field = clazz.getDeclaredField(name);
            }
        }

        if (field == null) {
            throw new NoSuchFieldException();
        }

        return field;
    }

    /**
     *
     * @param clazz Class
     *
     * @param includeSuperFields boolean
     *
     * @return List<Field>
     */
    public static List<Field> getDeclaredFields(Class clazz, boolean includeSuperFields) {
        List<Field> fields = new ArrayList<Field>();

        fields.addAll(Arrays.asList(clazz.getDeclaredFields()));

        // super Class field True
        if (includeSuperFields) {
            while ((clazz = clazz.getSuperclass()) != null) {
                fields.addAll(Arrays.asList(clazz.getDeclaredFields()));
            }
        }
        return fields;
    }

    /**
     *
     * @param clazz Class
     *
     * @param includeSuperFields boolean
     *
     * @return List<Method>
     */
    public static List<Method> getDeclaredMethods(Class clazz, boolean includeSuperMethods) {
        List<Method> methods = new ArrayList<Method>();
        methods.addAll(Arrays.asList(clazz.getDeclaredMethods()));
        // super Class field True
        if (includeSuperMethods) {
            while ((clazz = clazz.getSuperclass()) != null) {
                methods.addAll(Arrays.asList(clazz.getDeclaredMethods()));
            }
        }
        return methods;
    }
}
